﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Comp477Project
{
    class ParticleSystem1
    {
        // Particle Subclass
        public class Particle
        {
            // Particle Attributes
            public Vector3 position, direction;
            public Matrix /*projection, */world;
            //public List<Primitives3D.GeometricPrimitive> particle;
            public int type;
            public float size, rotX, rotZ, speed;
            public double lifetime, age;
            public bool dead;
            public Color color;
            public float spread;
            public float angle;
            //public int maxSpread;
            public float minSpeed;
            public float redFac;
            public float gravitationalConstant;
            public static Random rand = new Random();
            //public int elapsed;
            // Particle Methods
            public Particle(/*GraphicsDevice graphicsDevice, ref Matrix projection*/)
            {
                //this.projection = projection;
                this.type = 0;
                //Random rand = new Random();
                if (this.type > 3) this.type = rand.Next(4);
                //particle = new List<Primitives3D.GeometricPrimitive>();
                //particle.Add(new Primitives3D.CubePrimitive(graphicsDevice));
                //particle.Add(new Primitives3D.CylinderPrimitive(graphicsDevice, 1, 1, 8));
                //particle.Add(new Primitives3D.SpherePrimitive(graphicsDevice, 1, 4));
                //particle.Add(new Primitives3D.TorusPrimitive(graphicsDevice, 1, 0.333f, 8));
                this.size = 0.05f;
                //this.position = new Vector3(0, 0, 0);
                this.direction = new Vector3(0, 0, 0);
                this.world = Matrix.CreateScale(size) * Matrix.CreateTranslation(position);
                this.color = Color.Red;
                this.age = 0;
                this.lifetime = 1;
                this.rotX = 0;
                this.rotZ = 0;
                this.speed = 0.02f;
                this.dead = false;
                this.spread = 0;
                this.angle = 0;
                this.minSpeed = 0.01f;
                this.redFac = 0.02f;
                this.gravitationalConstant = 0.002f;
            }
            public void Reset(int type, Vector3 position, Vector3 direction, double lifetime, Color color,float sp,float ang,float minSpeed,float rFac,float gConstant)
            {
                this.type = type;
                //Random rand = new Random();
                if (this.type > 3) this.type = rand.Next(4);
                this.position = position;
                this.direction = direction;
                this.lifetime = lifetime;
                this.age = 0;
                this.rotX = 0;
                this.rotZ = 0;
                this.dead = false;
                this.color = color;
                this.speed = this.minSpeed + ((float)rand.NextDouble() * this.redFac);
                this.spread = sp;
                this.angle = ang;
                this.minSpeed = minSpeed;
                this.redFac = rFac;
                this.gravitationalConstant = gConstant;
            }
            public void Update(GameTime gameTime, bool gravity,float currType)
            {
                age += gameTime.ElapsedGameTime.TotalMilliseconds;
                if (gravity)
                {
                    direction.Y -= gravitationalConstant;
                    direction.Normalize();
                }
                //color.A--;
                position += speed * direction;
                
                rotX += 0.05f % MathHelper.ToRadians(360);
                rotZ += 0.1f % MathHelper.ToRadians(360);
                world = Matrix.CreateScale(size) * Matrix.CreateRotationX(rotX) * Matrix.CreateRotationZ(rotZ) * Matrix.CreateTranslation(position);
                if (age > lifetime)
                    dead = true;
                if (currType == 3)
                {
                    direction.Y -= 0.02f;
                    if (age > 2000 && dead == false)
                    {
                        color.G--;
                        size += 0.001f;
                      
                    }
                    else
                    {
                        size = 0.05f;
                        color = Color.OrangeRed;
                        
                    }

                   
                }
               
            }
            public void Draw(Matrix view, Matrix projection, ref List<Primitives3D.GeometricPrimitive> particleModel)
            {
                particleModel[type].Draw(world, view, projection, color);
            }
        }

      //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ PARTICLE SYSTEM @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        // System Attributes
        Vector3 position;
        Matrix projection;
        List<Particle> particles;
        List<Primitives3D.GeometricPrimitive> particleModel;
        int n, currentType;
        float mapSize;
        double duration, timer;
        static Random rand = new Random();
        bool gravity;
        float spread;
        float angle;
        const int maxSpread=200;
        public float minSpeed;
        public float redFac;
        public float gravitationalConstant;
        GraphicsDevice graphicsDevice;
        // System Methods
        public ParticleSystem1(GraphicsDevice graphicsDevice, ref Matrix projection, int type)
        {
            // Initialize Attributes
            this.graphicsDevice = graphicsDevice;
            particles = new List<Particle>();
            this.projection = projection;
            this.mapSize = 5;
            this.n =10000;
            this.duration = 3000;
            this.timer = 0;
            this.gravity = true;
            this.minSpeed = 0.01f;
            this.redFac = 0.02f;
            this.gravitationalConstant = 0.02f;
            this.currentType = type;
            particleModel = new List<Primitives3D.GeometricPrimitive>();
            particleModel.Add(new Primitives3D.CubePrimitive(graphicsDevice));
            particleModel.Add(new Primitives3D.CylinderPrimitive(graphicsDevice, 1, 1, 8));
            particleModel.Add(new Primitives3D.SpherePrimitive(graphicsDevice, 1, 4));
            particleModel.Add(new Primitives3D.TorusPrimitive(graphicsDevice, 1, 0.333f, 8));
            // Generate System
            double x = rand.NextDouble() * mapSize - (mapSize / 2);
            double y = rand.NextDouble() * (mapSize / 2);
            double z = rand.NextDouble() * mapSize - (mapSize / 2);
            // this.position = new Vector3((float)x, (float)y, (float)z);
            for (int i = 0; i != n; i++)
            {
                //int t = 0;
                //Vector3 direction = new Vector3((float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1));
                //double lifetime = duration - (rand.NextDouble() * (duration / 2));
                //if (type == 0)
                //{
                //    t = 4;
                //    gravity = true;
                //}
                //direction.Normalize();
                particles.Add(new Particle(/*graphicsDevice, ref projection*/));
            }
        }
        public void Reset(int type)
        {
            this.currentType = type;
            // Generate System
            double x = rand.NextDouble() * mapSize - (mapSize / 2);
            double y = rand.NextDouble() * (mapSize / 3) + (mapSize / 4);
            double z = rand.NextDouble() * mapSize - (mapSize / 2);
            //this.position = new Vector3((float)x, (float)y, (float)z);
            for (int i = 0; i != n; i++)
            {
                int t = 0;
                Vector3 direction = new Vector3((float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1));
                double lifetime = duration - (rand.NextDouble() * (duration / 2));
                Color color = Color.White;
                if (type == 0)
                {
                    t = 4;
                    gravity = true;
                    color.R = (byte)(rand.NextDouble() * 254);
                    color.G = (byte)(rand.NextDouble() * 254);
                    color.B = (byte)(rand.NextDouble() * 254);
                }
                if (type == 1)
                {
                    t = 2;
                    gravity = true;
                    direction.Y = -1;
                    double xp = rand.NextDouble() * mapSize - (mapSize / 2);
                    double yp = rand.NextDouble() * (mapSize / 3) + (mapSize / 4);
                    double zp = rand.NextDouble() * mapSize - (mapSize / 2);
                    this.position = new Vector3((float)xp, (float)yp, (float)zp);
                }
                if (type == 2)
                {
                    t = 2;
                    gravity = true;
                    this.position = new Vector3(0.35f, 1.5f, 1);
                    color = Color.LightBlue;
                    gravity = true;
                    direction = new Vector3((float)(rand.NextDouble() * 1 - 0.5f), 1, (float)(rand.NextDouble() * 1 - 0.5f));
                    //this.position = new Vector3(0, 0, 0);
                }
                if (type == 3)
                {
                    t = 2;
                    color = Color.OrangeRed;
                    gravity = true;
                    this.position = new Vector3(0.35f, 1.5f, 1);
                    this.spread = (float)(rand.Next(maxSpread)) / 10000.0f;
                    this.angle = (float)(rand.Next(157)) / 100.0f;
                    direction.X = (float)Math.Cos(angle) * spread;
                    direction.Z = (float)Math.Sin(angle) * spread;
                    if (rand.Next() % 2 == 0)
                        direction.X = -direction.X;
                    if (rand.Next() % 2 == 0)
                        direction.Z = -direction.Z;
                    direction.Y = rand.Next(500) / 10000.0f;
                }
                direction.Normalize();
                //particles.Add(new Particle(graphicsDevice, ref projection, t, position, direction, lifetime));
                particles[i].Reset(t, position, direction, lifetime, color,spread,angle,this.minSpeed,this.redFac,this.gravitationalConstant);
            }
        }
        public void Update(GameTime gameTime)
        {
            if (currentType == 0)
            {
                timer += gameTime.ElapsedGameTime.TotalMilliseconds;
                if (timer > duration + 500)
                {
                    this.Reset(currentType);
                    timer = 0;
                }
            }
            if (currentType == 1)
            {
                foreach (Particle p in particles)
                {
                    if (p.dead)
                    {
                        Vector3 direction = new Vector3((float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1), (float)(rand.NextDouble() * 2 - 1));
                        double lifetime = duration - (rand.NextDouble() * (duration / 2));
                        Color color = Color.White;
                        int t = 2;
                        direction.Y = -1;
                        double xp = rand.NextDouble() * mapSize - (mapSize / 2);
                        double yp = rand.NextDouble() * (mapSize / 3) + (mapSize / 4);
                        double zp = rand.NextDouble() * mapSize - (mapSize / 2);
                        this.position = new Vector3((float)xp, (float)yp, (float)zp);
                        direction.Normalize();
                        p.Reset(t, position, direction, lifetime, color,0,0,this.minSpeed,this.redFac,this.gravitationalConstant);
                    }
                }
            }
            if (currentType == 2)
            {
                foreach (Particle p in particles)
                {
                    if (p.dead)
                    {
                        Vector3 direction = new Vector3((float)(rand.NextDouble() * 1 - 0.5f), 1, (float)(rand.NextDouble() * 1 - 0.5f));
                        double lifetime = duration - (rand.NextDouble() * (duration / 2));
                        Color color = Color.LightBlue;
                        int t = 2;
                        this.position = new Vector3(0, 0, 0) + new Vector3(0.35f, 1.5f, 1);
                        direction.Normalize();
                        p.Reset(t, position, direction, lifetime, color,0,0,this.minSpeed,this.redFac,this.gravitationalConstant);
                    }
                }
            }
            if (currentType == 3)
            {
                foreach (Particle p in particles)
                {
                    if (p.dead)
                    {
                        
                        double lifetime = duration - (rand.NextDouble() * (duration / 2));

                        this.gravity = true;
                        Vector3 direction = new Vector3(0, 0, 0);
                        int t =2;
                        Color color = Color.OrangeRed;
                        this.position = new Vector3(0.35f, 1.5f, 1);
                        this.spread = (float)(rand.Next(maxSpread)) / 1000.0f;
                        this.angle = (float)(rand.Next(100)) / 100.0f;
                        direction.X = (float)Math.Cos(angle) * spread;
                        direction.Z = (float)Math.Sin(angle) * spread;
                        if (rand.Next() % 2 == 0)
                            direction.X = -direction.X;
                        if (rand.Next() % 2 == 0)
                            direction.Z = -direction.Z;
                        direction.Y = rand.Next(8000) / 10000.0f;
                        direction.Normalize();
                        p.Reset(t, position, direction, lifetime, color, 0, 0,this.minSpeed,this.redFac,0.00005f);
                    }
                }
            }
            foreach (Particle p in particles)
            {
                if (!p.dead)
                    p.Update(gameTime, gravity,currentType);
            }
        }
        public void Draw(Matrix view)
        {
            foreach (Particle p in particles)
            {
                if (!p.dead)
                    p.Draw(view, projection, ref particleModel);
            }
        }
    }
}
